//////////////////////////////////////////////////////////////////////////////
//
// HOW SIGNALS AND SLOTS WILL WORK: BnetSpy.cpp / BnetSpy.h
//  
// Long arrow is "gets-connected-to":                 "------>"
// Long broken array is "gets-disconnected-from":     "---/-->"
//
// Long array, double head is "emits":                "----->>"
//
//
// SIGNAL                        |     SLOT                        |
// --------------------------          ---------------------------
//
// this->defaultButtonPressed -------> this->runStarcraft
//
// this->applyButtonPressed   -------> this->applyClicked
//
// this->writeJoinGame        -------> regEditor_->writeJoinGame
// ^
// ^
// |
// |<<------- <<----- <<----- <<----- <<----- <<----- <<----- <<----- <<---|
//                                                                         |
//                                                                         |
// logger_->logging           -------> this->parseForJoinHelp      ----->> |
//      "                     ---/--->         "
//
//
// The only signals that get connected and disconnected after
// initialization is between SIGNAL logger_->logging(BnetMessage) and
// SLOT parseForJoinHelp(BnetMessage).  This occurs when the user
// chooses to enable or disable join help.
//
//////////////////////////////////////////////////////////////////////////////

#include <qapplication.h>  // So we can post a "None" event to update signals.
#include <qmessagebox.h>
#include <qfile.h>

#include "BnetSpy.h"


BnetSpy::BnetSpy( QWidget * parent, 
		  const char * name, 
		  bool modal, 
		  WFlags f )
    : 
    QTabDialog( parent, name, modal, f ),
    saveDataFilename( "bnetspy.dat" ),
    FileVersion( 5 )
{
    // Set caption and icon.
    this->setCaption( "BnetSpy" );
    this->setIcon( QPixmap::fromMimeSource("5POINT.png") );

    // Create tabs.
    bnetJoinHelpTab = new QTab( tr("Bnet Join Help") );
    bnetLoggerTab   = new QTab( tr("Bnet Logger") );
    aboutTab        = new QTab( tr("About BnetSpy") );
    
    // Create widgets that will go inside the tabs.
    joinHelpWidget = new BnetJoinHelpSettingsWidget( this );
    loggerWidget   = new BnetLoggerSettingsWidget( this );
    aboutWidget    = new AboutWidget( this );

    // Add the tabs.
    this->addTab( joinHelpWidget, bnetJoinHelpTab );
    this->addTab( loggerWidget,   bnetLoggerTab );
    this->addTab( aboutWidget,    aboutTab );
    
    // Add default buttons.
    this->setDefaultButton( tr("&Run Starcraft") );
    this->setApplyButton( tr("&Apply") );
    this->setOkButton( tr("E&xit") );

    // QProcess that can spawn Starcraft.
    starcraftProcess_ = new QProcess(this);

    // Logger object.
    logger_ = new BnetLogger( this );
    logger_->setLogging( true );       // Always log, even if it's not
				       // to a file.

    // Registry editor.
    regEditor_ = new StarcraftRegistryEditor( this );

    // Connect signals to slots.  See top for how signals and slots
    // are set up in this class.
    connect( this, SIGNAL(defaultButtonPressed()),
	     this, SLOT(runStarcraft()) );
    connect( this, SIGNAL(applyButtonPressed()),
	     this, SLOT(applyClicked()) );
    connect( this, SIGNAL(writeJoinGame(const QString&)),
	     regEditor_, SLOT(writeJoinGame(const QString&)) );

    // Load the settings and apply.
    loadSettings();
    applyClicked();

    // Start the logger thread that lives till it destructs, which is
    // when this object destructs.
    logger_->start();

    // Test to make sure the reading of previous game joined worked.
//     QMessageBox::information( this, "DEBUG", QString("previous game was: " )
// 			      + regEditor_->readJoinGame() );
}


// See top for how signals and slots are all set up in this class.
void BnetSpy::applyClicked()
{
    saveSettings();
    
    logger_->setLoggingToFile( loggerEnabled_, 
			       loggerFilename_,
			       logUserInChannelEnabled_,
			       logUserJoinLeaveEnabled_ );
    
    if( joinHelpEnabled_ )
    {
	// With Qt, many connections can be made between the same
	// signal and slot.  It will mean that two executions of the
	// connected slot would be executed each time the signal
	// fires.  

	// In our situation, at most, only 1 connection will be made.
	// Here I will disconnect between two (if it is connected),
	// and then connect.  If they were not connected in the first
	// place, then the disconnect would fail, and then the
	// following connect would occur.  If it already were
	// connected, then it will just do a disconnect and connect
	// that will negate each other... leaving the signal connected
	// to the slot.
	disconnect( logger_, SIGNAL(logging(BnetMessage)),
		    this, SLOT(parseForJoinHelp(BnetMessage)) );
	connect( logger_, SIGNAL(logging(BnetMessage)),
		 this, SLOT(parseForJoinHelp(BnetMessage)));
    }
    else 
    {
	disconnect( logger_, SIGNAL(logging(BnetMessage)),
		    this, SLOT(parseForJoinHelp(BnetMessage)) );
    }
}

// Spawns starcraft as a process.
void BnetSpy::runStarcraft()
{
    QString app("C:\\Program Files\\Starcraft\\Starcraft.exe");
    
    starcraftProcess_->clearArguments();
    starcraftProcess_->addArgument( app );
 
    if(! starcraftProcess_->start() )
    {
	QMessageBox::warning( this,
			      tr("Spawn process failed!"),
			      tr("Failed to open application ") + app,
			      QMessageBox::Ok,
			      QMessageBox::NoButton,
			      QMessageBox::NoButton );
    }
}

// Loads the settings.
bool BnetSpy::loadSettings()
{
    // If there are no saved settings, then don't load anything new.
    if( !QFile::exists( saveDataFilename ) )
    {
	return false;
    }

    // Try to open the file.
    QFile file( saveDataFilename );
    if( !file.open( IO_ReadOnly ) )
    {
	// Problems opening the file.
	QMessageBox::warning( this, tr("Load Failed!"),
			      tr("Could not open ") + saveDataFilename +
			      tr(" for loading saved settings.") );
	return false;
    }
    
    // Create a QDataStream for reading in from.
    QDataStream in( &file );
    
    // Read the magic number to make sure it's a good file.
    Q_UINT32 fileMagicNumber;
    Q_UINT32 fileVersionNumber;

    in >> fileMagicNumber;
    
    if( fileMagicNumber != MagicNumber )
    {
	QMessageBox::warning( this, tr("Load Failed!"),
			      tr("The saved data file ") + saveDataFilename +
			      tr(" is corrupt.") );
	return false;
    }
    
    in >> fileVersionNumber;
    
    if( fileVersionNumber != FileVersion )
    {
	QMessageBox::warning( this, tr("Load Failed!"),
			      tr("The version number of the saved datafile ")
			      + saveDataFilename + tr(" is wrong.") );
	return false;
    }
    

    // Load the saved data.

    // Join help enabled.
    joinHelpEnabled_ = this->loadBool( in );
    // Logger Enabled.
    loggerEnabled_ = this->loadBool( in );
    // Logging User-in-channel enabled.
    logUserInChannelEnabled_ = this->loadBool( in );
    // Logging User-join-leave enabled.
    logUserJoinLeaveEnabled_ = this->loadBool( in );
    // Logger Filename.
    in >> loggerFilename_;

    // Set the widgets.
    joinHelpWidget->setEnabled( joinHelpEnabled_ );
    loggerWidget->setEnabled( loggerEnabled_ );
    loggerWidget->setFilename( loggerFilename_ );

    return true;
}

// Saves the settings.
bool BnetSpy::saveSettings()
{
    // Grab the values from the widgets.
    joinHelpEnabled_ = joinHelpWidget->getEnabled();
    loggerEnabled_ = loggerWidget->getEnabled();
    logUserInChannelEnabled_ = loggerWidget->getLogUserInChannel();
    logUserJoinLeaveEnabled_ = loggerWidget->getLogUserJoinLeave();
    loggerFilename_ = loggerWidget->getFilename();

    // Open the file as write.
    QFile file( saveDataFilename );
    if( !file.open( IO_WriteOnly ) )
    {
	QMessageBox::warning( this, tr("Save Failed!"),
			      tr("Could not open file ") + saveDataFilename +
			      tr(" for saving settings.") );
	return false;
    }
    
    QDataStream out( &file );
    out.setVersion( 5 );
    out << (Q_UINT32)MagicNumber;
    out << (Q_UINT32)FileVersion;
    out << (Q_UINT8)joinHelpEnabled_;
    out << (Q_UINT8)loggerEnabled_;
    out << (Q_UINT8)logUserInChannelEnabled_;
    out << (Q_UINT8)logUserJoinLeaveEnabled_;    
    out << loggerFilename_;

    if( file.status() != IO_Ok )
    {
	QMessageBox::warning( this, tr("Save Failed!"),
			      tr("Error writing settings to file ") +
			      saveDataFilename );
	return false;
    }

    return true;
}

// See top of file for how signals and slots are all set up in this class.
void BnetSpy::parseForJoinHelp( BnetMessage msg )
{
    if( msg.type == BnetMessage::FriendJoinedGame )
    {
	// Emit the signal.
	emit writeJoinGame(msg.gameName);
	// Not sure if this is needed, but I read somewhere it
	// might negate any thread delays when using an emit.
	QApplication::postEvent( qApp, new QEvent( QEvent::None ) );
    }
}


// Load a boolean value (stored as a Q_UINT8) from the QDataStream.
// Make sure that there is enough in the stream to read this value in!
bool BnetSpy::loadBool( QDataStream& in )
{
    // Temp variable.
    Q_UINT8 someBoolean;

    in >> someBoolean;

    if( someBoolean == 0 )
    {
	return false;
    }
    else
    {
	return true;
    }
}

